//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#ifndef NJ_THREADS_SYSTEM_H
#	define NJ_THREADS_SYSTEM_H

#	include "NjExample01.h"
#	include "NjGenericStaticInterface.h"

	/// \brief Interface to kernel thread features
	class NjThreadsSystem : public NjGenericStaticInterface
	{
	public:
		/// \brief Opaque class reppresenting the thread
		class Thread;

		/// \copydoc NjEventsSystem::TimeMs
		typedef NjEventsSystem::TimeMs TimeMs;

		/// \copydoc NjEventsSystem::TryResult
		typedef NjEventsSystem::TryResult TryResult;

		/// \brief The function reppresenting a thread
		typedef void (* ThreadFunct) (void * pData);

		/// \brief Yeld call possible results
		enum YeldResult
		{
			/// \brief Yeld call failed
			YELD_FAILURE,

			/// \brief Yeld call succeeded and switched to another thread
			YELD_SWITCHED_SUCCESS,

			/// \brief Yeld call succeeded, but there were no threads ready to run
			YELD_NOT_SWITCHED_SUCCESS
		};

		/// Abstract class reppresenting the NjThreadsSystem implementation
		class Implementation : public NjGenericStaticInterface::Implementation
		{
			friend class NjThreadsSystem;

		public:
			NJ_TRIVIAL_VIRTUAL_DESTRUCTOR(Implementation);

		protected:
			NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(Implementation);

			/// \brief Creates a thread
			///
			/// \param pfnThread The function that will be run by the thread
			/// \param pData The parameter that will be passed to \em pfnThread
			/// \return The created thread or NJ_NULL on internal error/no more objects available
			virtual Thread * CreateThread(ThreadFunct pfnThread, void * pData) = 0;

			/// \brief Waits for a thread completion
			///
			/// If the thread is completes before \em uTimeMs expires, the function will return TRY_SUCCESS.
			/// If the thread is still running after \em uTimeMs expires, the function will return TRY_TIMEOUT. 
			/// In case of internal error TRY_FAILURE will be returned. 
			/// If \m uTimeMs is set to NjEventsSystem::WAIT_FOREVER the function will never time out.
			/// This means that in this case just TRY_SUCCESS or TRY_FAILURE can be returned
			/// \param pThread the thread to wait for
			/// \param uTimeMs the maximum wait time expressed in milliseconds
			virtual TryResult TryJoinThread(Thread * pThread, TimeMs uTimeMs) = 0;

			/// \brief Destroys a previously created thread \em pThread
			///
			/// Once this function is called the pointer \em pThread cannot be any more used. The usage of such pointer has undefined behaviour.
			/// This function should be called both if the thread is still running and if the thread already completed as it releases internal resources
			/// \return The function will return NJ_TRUE on success or NJ_FALSE in case of internal error
			virtual NjBool DestroyThread(Thread * pThread) = 0;

			/// \brief Suspends thre thread for \em uTimeMs milliseconds
			/// \return The function will return NJ_TRUE on success or NJ_FALSE in case of internal error
			virtual NjBool Sleep(TimeMs uTimeMs) = 0;

			/// \brief Causes the calling thread to yield execution to another thread that is ready to run
			///
			/// If other threads are ready to run it will return YELD_SWITCHED_SUCCESS
			/// If no other threads are ready to run it will return YELD_NOT_SWITCHED_SUCCESS
			/// In case of internal error YELD_FAILURE will be returned. 
			virtual YeldResult Yeld() = 0;

		private:
			NJ_DISABLE_COPY(Implementation);
		};

		/// \copydoc NjThreadsSystem::Implementation::CreateThread
		static Thread * CreateThread(ThreadFunct pfnThread, void * pData);

		/// \copydoc NjThreadsSystem::Implementation::TryJoinThread
		static TryResult TryJoinThread(Thread * pThread, TimeMs uTimeMs);

		/// \brief Waits for a thread completion
		///
		/// This function is similar to TryJoinThread but it cannot timeout
		/// \return NJ_TRUE if the wait succeed or NJ_FALSE if internally fails
		static NjBool JoinThread(Thread * pThread);

		/// \copydoc NjThreadsSystem::Implementation::DestroyThread
		static NjBool DestroyThread(Thread * pThread);

		/// \copydoc NjThreadsSystem::Implementation::Sleep
		static NjBool Sleep(TimeMs uTimeMs);

		/// \copydoc NjThreadsSystem::Implementation::Yeld
		static YeldResult Yeld();

		NJ_DECLARE_STATIC_INTERFACE_METHODS();

	private:
		NJ_DISABLE_INSTANCE(NjThreadsSystem);
	};

#	include "NjThreadsSystem_inline.h"

#endif // NJ_THREADS_SYSTEM_H
